﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.Reflection;
using SharedDataContracts;
using CommonDataContracts;

namespace BacteriumObserver
{
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single,
                     ConcurrencyMode = ConcurrencyMode.Multiple)]
    public class BacteriumObserver : IBacteriumObserver
    {
        private int _ts = 0;
        private Dictionary<String, BacteriumProfile> _bacteria;
        
        internal Dictionary<String, BacteriumProfile> Bacteria
        {
            get { return _bacteria; }
            set { _bacteria = value; }
        }

        public Object CS { get; set; }
        

        public BacteriumObserver()
        {
            
        }

        #region Observer Methods

        
        public void RegisterWithDomain(String endpoint, BacteriumSnapshot b)
        {
            var messageType = "RegisterWithDomain";

            Console.WriteLine("{0}: {1} call \nfrom {2}",
                "Observer",
                messageType,
                endpoint);

            //lock (CS)
            //{
                if (!Bacteria.ContainsKey(endpoint))
                {
                    Bacteria.Add(endpoint, new BacteriumProfile(b));

                    Console.WriteLine("  New Bacterium Registered");
                    foreach (var prop in typeof(BacteriumSnapshot).GetProperties())
                    {
                        Console.WriteLine("    {0}: {1} ", prop.Name, prop.GetValue(b, null));
                    }
                //}
               

                Bacteria[endpoint].Events.AppendEvent(new BacteriumEventDescriptor(messageType,                        
                        null), (++_ts) / (_bacteria.Count - (_bacteria.Count > 1 ? 1 : 0)));
            }
        }

        
        public void UpdateBacteriumProperty(String endpoint, String name, String value)
        {
            var messageType = "UpdateBacteriumProperty - " + name;           
           

            Console.WriteLine("{0}: {1} call \nfrom {2}",
                "Observer",
                messageType,
                endpoint);

            //lock (CS)
            //{
                
                if (Bacteria.ContainsKey(endpoint))
                {
                    ++_ts;
                    var b = Bacteria[endpoint].Snapshot;
                    var prop = typeof(BacteriumSnapshot).GetProperty(name);

                    //Generically determines property type and casts the value to that type..enumeration type requires special conversion
                    prop.SetValue(b,
                                  prop.PropertyType == typeof(BacteriumAction) 
                                  ? (BacteriumAction)Enum.Parse(typeof(BacteriumAction), value) 
                                  : Convert.ChangeType(value, prop.PropertyType),
                                  null);


                    Bacteria[endpoint].Events.AppendEvent(new BacteriumEventDescriptor(messageType,                        
                        null));
                }
            //}
        }

        
        public String GetNeutralBacterium(String endpoint)
        {
            var messageType = "GetNeutralBacterium";
            var response = String.Empty;

            Console.WriteLine("{0}: {1} call \nfrom {2}",
                "Observer",
                messageType,
                endpoint);

            //lock (CS)
            //{
                
                if (Bacteria.ContainsKey(endpoint))
                {
                    ++_ts;
                    var neutrals = Bacteria.Where(x => x.Value.Snapshot.TeamID == 0);

                    if (neutrals.Count() != 0)                    
                        response = neutrals.First().Key;

                    Bacteria[endpoint].Events.AppendEvent(new BacteriumEventDescriptor(messageType,
                        !String.IsNullOrEmpty(response) 
                        ? Bacteria[response].Snapshot.ID.ToString()
                        : response));
                    
                }

                
            //}
                return response;
            
        }

       
        public Boolean GetTeamBalance(String endpoint)
        {
            var messageType = "GetTeamBalance";
            var response = false;

            Console.WriteLine("{0}: {1} call \nfrom {2}",
                "Observer",
                messageType,
                endpoint);

            //lock (CS)
            //{
                
                if (Bacteria.ContainsKey(endpoint))
                {
                    ++_ts;
                    var teamID = Bacteria[endpoint].Snapshot.TeamID;

                    var allies = GetAllies(teamID);
                    var enemies = GetEnemies(teamID);

                    if (allies.Count() > enemies.Count())
                        response = true;

                    Bacteria[endpoint].Events.AppendEvent(new BacteriumEventDescriptor(messageType,                        
                        response.ToString()));
                }

                
            //}
                return response;
            

        }

        
        public String GetWeakestEnemy(String endpoint)
        {
            var messageType = "GetWeakestEnemy";
            var response = String.Empty;

            Console.WriteLine("{0}: {1} call \nfrom {2}",
                "Observer",
                messageType,
                endpoint);

           
            //lock (CS)
            //{
                ++_ts;
                if (Bacteria.ContainsKey(endpoint))
                {
                    var teamID = Bacteria[endpoint].Snapshot.TeamID;

                    var enemies = GetEnemies(teamID);

                    if (enemies.Count() != 0)                   
                        response = enemies.OrderBy(x => x.Value.Snapshot.Power).First().Key;

                    Bacteria[endpoint].Events.AppendEvent(new BacteriumEventDescriptor(messageType,                        
                        !String.IsNullOrEmpty(response)
                        ? Bacteria[response].Snapshot.ID.ToString()
                        : response));
                }

                
            //}
            return response;
        }

        
        public Boolean GetPowerRatio(String endpoint)
        {
            var messageType = "GetPowerRatio";
            var response = false;

            Console.WriteLine("{0}: {1} call \nfrom {2}",
                "Observer",
                messageType,
                endpoint);

            //lock (CS)
            //{
               
                if (Bacteria.ContainsKey(endpoint))
                {
                    ++_ts;
                    var enemies = GetEnemies(Bacteria[endpoint].Snapshot.TeamID);
                    var allies = GetAllies(Bacteria[endpoint].Snapshot.TeamID);

                    var sumEnemyPower = enemies.Sum(b => b.Value.Snapshot.Power);
                    var sumAlliedPower = allies.Sum(b => b.Value.Snapshot.Power);

                    if (sumAlliedPower > sumEnemyPower)
                        response = true;

                    Bacteria[endpoint].Events.AppendEvent(new BacteriumEventDescriptor(messageType,                        
                        response.ToString()));
                }

                
            //}
            return response;
        }

        
        public String GetWeakestAlly(String endpoint)
        {
            var messageType = "GetWeakestAlly";
            var response = String.Empty;

            Console.WriteLine("{0}: {1} call \nfrom {2}",
                "Observer",
                messageType,
                endpoint);

            //lock (CS)
            //{
               
                if (Bacteria.ContainsKey(endpoint))
                {
                    ++_ts;
                    var alliesExcludingCaller = GetAllies(Bacteria[endpoint].Snapshot.TeamID).Where(a => a.Key != endpoint);
                    
                    
                    if (alliesExcludingCaller.Count() != 0)
                        response = alliesExcludingCaller.OrderBy(x => x.Value.Snapshot.Power).First().Key;

                    Bacteria[endpoint].Events.AppendEvent(new BacteriumEventDescriptor(messageType,                         
                         !String.IsNullOrEmpty(response)
                         ? Bacteria[response].Snapshot.ID.ToString()
                         : response));                    
                }

                
            //}
            return response;
        }

        #endregion

        
        internal void UnregisterFromDomain(String endpoint)
        {
            lock (CS)
            {
                Bacteria.Remove(endpoint);
                Console.WriteLine("{0} has been unregistered.", endpoint);
            }
            
        }

        #region Observer Routines and Shortcuts

        private IEnumerable<KeyValuePair<String, BacteriumProfile>> GetEnemies(int teamID)
        {
            return Bacteria.Where(x => x.Value.Snapshot.TeamID != teamID && x.Value.Snapshot.TeamID != 0);
        }

        private IEnumerable<KeyValuePair<String, BacteriumProfile>> GetAllies(int teamID)
        {
            return Bacteria.Where(x => x.Value.Snapshot.TeamID == teamID);
        }

        private Boolean IsBacteriumEndpointOk(String endpoint)
        {
            return true;
        }

        #endregion



        
    }
}
